package com.yykj.hospital.server.service.impl;

import com.google.common.base.Joiner;
import com.yykj.base.client.dto.WordBriefInfo;
import com.yykj.base.client.feign.BaseFeignClient;
import com.yykj.common.constant.CommonConstant;
import com.yykj.common.constant.RedisConstant;
import com.yykj.common.enums.HospitalAdminType;
import com.yykj.common.enums.ResultEnums;
import com.yykj.common.exception.FeignServerException;
import com.yykj.common.exception.NormalException;
import com.yykj.common.service.RedisService;
import com.yykj.common.util.*;
import com.yykj.common.vo.PageData;
import com.yykj.hospital.client.dto.HospitalAdminDTO;
import com.yykj.hospital.client.form.HospitalAdminListForm;
import com.yykj.hospital.server.converter.HospitalAdminDTOConverter;
import com.yykj.hospital.server.converter.HospitalAdminListDTOConverter;
import com.yykj.hospital.client.dto.HospitalAdminListDTO;
import com.yykj.hospital.server.dto.AdminPasswordDTO;
import com.yykj.hospital.server.dto.HospitalAdminMenuDTO;
import com.yykj.hospital.server.dto.HospitalAdminSubMenu;
import com.yykj.hospital.server.dto.HospitalLoginInfo;
import com.yykj.hospital.server.enums.OperationEnums;
import com.yykj.hospital.client.form.HospitalAdminEditForm;
import com.yykj.hospital.server.form.HospitalAdminLoginForm;
import com.yykj.hospital.server.model.entity.HospitalAdmin;
import com.yykj.hospital.server.model.entity.HospitalAdminLog;
import com.yykj.hospital.server.model.entity.HospitalAdminRole;
import com.yykj.hospital.server.model.entity.HospitalMenu;
import com.yykj.hospital.server.model.repository.HospitalAdminLogRepository;
import com.yykj.hospital.server.model.repository.HospitalAdminRepository;
import com.yykj.hospital.server.model.repository.HospitalAdminRoleRepository;
import com.yykj.hospital.server.model.repository.HospitalMenuRepository;
import com.yykj.hospital.server.service.HospitalAdminService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class HospitalAdminServiceImpl implements HospitalAdminService {

    @Override
    public HospitalLoginInfo login(HospitalAdminLoginForm form) {

        HospitalAdmin admin = adminRepository.findFirstByUsername(form.getUsername());

        if (admin == null || !admin.getPassword().equals(LocalStringUtil.encrypt(form.getPassword()))) {
            throw new NormalException(ResultEnums.LOGIN_FAIL);
        }

        HospitalLoginInfo info = new HospitalLoginInfo();
        BeanUtils.copyProperties(admin, info);
        info.setAdminId(admin.getId());
        String token = LocalStringUtil.getUUID();
        info.setToken(token);
        info.setMenuList(getHospitalAdminMenu(admin.getId()));
        info.setAdminId(admin.getId());
        
        redisService.set(token, GsonUtil.toJson(info), RedisConstant.LOGIN_EXPIRE);

        logRepository.save(HospitalAdminLog.builder()
                .username(admin.getUsername())
                .nickname(admin.getNickname())
                .remark("登录成功")
                .msg(OperationEnums.ACCOUNT_LOGIN_IN.getMsg())
                .build());



        return info;
    }


    @Override
    public HospitalLoginInfo loginInfo(String token) {
        String s = redisService.get(token);
        if(StringUtils.isEmpty(s)){
            throw new NormalException(ResultEnums.HEAD_TOKEN_ERROR);
        }
        HospitalLoginInfo info = GsonUtil.fromJson(s, HospitalLoginInfo.class);
        redisService.set(token, s, RedisConstant.LOGIN_EXPIRE);
        return info;
    }

    @Override
    public void logout() {
        String token = LocalRequestUtil.getToken();
        redisService.remove(token);
    }

    @Override
    public HospitalAdminDTO editAdmin(HospitalAdminEditForm form) {

        HospitalAdmin hospitalAdmin;
        if (form.getId() != null && form.getId() > 0) {//修改
            hospitalAdmin = adminRepository.findById(form.getId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
            form.setUsername(null);//username 不能修改
            LocalObjectUtil.copyNullValue(form, hospitalAdmin);
            if (!StringUtils.isEmpty(form.getPassword())) {
                hospitalAdmin.setPassword(LocalStringUtil.encrypt(form.getPassword()));
            }
        } else {
            hospitalAdmin = new HospitalAdmin();
            if (StringUtils.isEmpty(form.getUsername())) {
                throw new FeignServerException(ResultEnums.PARAM_ERROR);
            }
            HospitalAdmin oldAdmin = adminRepository.findFirstByUsername(form.getUsername());
            if (oldAdmin != null) {
                throw new FeignServerException(ResultEnums.ALL_READY_EXIST, "username 已经存在");
            }
            if (form.getHospitalId() == null) {
                String token = LocalRequestUtil.getToken();
                HospitalLoginInfo info = GsonUtil.fromJson(redisService.get(token), HospitalLoginInfo.class);
                form.setHospitalId(info.getHospitalId());
            }
            LocalObjectUtil.copyNullValue(form, hospitalAdmin);

            if (StringUtils.isEmpty(form.getPassword())) {
                hospitalAdmin.setPassword(LocalStringUtil.encrypt(CommonConstant.DEFAULT_PASSWORD));
            } else {
                hospitalAdmin.setPassword(LocalStringUtil.encrypt(form.getPassword()));
            }
        }

        if (form.getRoles() != null) {
            hospitalAdmin.setRoleIds(Joiner.on(",").join(form.getRoles()));
        }
        if (!StringUtils.isEmpty(form.getNickname())) {
            WordBriefInfo wordBriefInfo = baseFeignClient.getWordBriefInfo(form.getNickname());
            hospitalAdmin.setWb(wordBriefInfo.getWb());
            hospitalAdmin.setPy(wordBriefInfo.getPy());
        } else {
            hospitalAdmin.setWb("");
            hospitalAdmin.setPy("");
        }

        hospitalAdmin = adminRepository.save(hospitalAdmin);
        return HospitalAdminDTOConverter.convert(hospitalAdmin);

    }

    @Override
    public void deleteAdmin(Integer adminId) {
        HospitalAdmin hospitalAdmin = adminRepository.findById(adminId).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        adminRepository.delete(hospitalAdmin);
    }


    @Override
    public List<HospitalAdminListDTO> listHospitalAdmin(HospitalAdminListForm form) {
        List<HospitalAdmin> list = adminRepository.findAll(generateSpecification(form));
        return HospitalAdminListDTOConverter.convert(list);
    }

    @Override
    public PageData<HospitalAdminListDTO> adminList(HospitalAdminListForm form) {
        PageRequest pageRequest = PageRequest.of(PageRequestUtil.getPage(form.getCurrentPage()), PageRequestUtil.getSize(form.getPageSize()));
        Page<HospitalAdmin> list = adminRepository.findAll(generateSpecification(form), pageRequest);
        PageData<HospitalAdminListDTO> result = new PageData<HospitalAdminListDTO>();
        result.setList(HospitalAdminListDTOConverter.convert(list.getContent()));
        PageData<HospitalAdmin> hospitalAdminPageData = PageData.formatList(list, PageRequestUtil.getPage(form.getCurrentPage()), PageRequestUtil.getSize(form.getPageSize()));
        BeanUtils.copyProperties(hospitalAdminPageData, result);
        result.setList(HospitalAdminListDTOConverter.convert(list.getContent()));
        List<Integer> roleIdList = new ArrayList<>();
        for (HospitalAdmin admin : list) {
            String roleIds = admin.getRoleIds();
            if (!StringUtils.isEmpty(roleIds)) {
                String[] split = roleIds.split(",");
                for (String str : split) {
                    roleIdList.add(Integer.parseInt(str));
                }
            }
        }
        List<HospitalAdminRole> allByIdIn = roleRepository.findAllByIdIn(roleIdList);
        Map<Integer, HospitalAdminRole> map = new HashMap<>();
        for (HospitalAdminRole role : allByIdIn) {
            map.put(role.getId(), role);
        }
        for (HospitalAdminListDTO dto : result.getList()) {
            if (!StringUtils.isEmpty(dto.getRoleIds())) {
                String[] split = dto.getRoleIds().split(",");
                for (String str : split) {
                    dto.setRoleName(dto.getRoleName() + " " + map.get(Integer.parseInt(str)).getName());
                }
            }
            dto.setRoleName(StringUtils.trimLeadingWhitespace(dto.getRoleName()));
        }
        return result;
    }

    @Override
    public List<HospitalAdminMenuDTO> getHospitalAdminMenu(Integer adminId) {

        HospitalAdmin admin = adminRepository.findById(adminId).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        List<HospitalMenu> menuList  ;
        if (HospitalAdminType.admin.equals(admin.getType())) {
            menuList =  menuRepository.findAllByHospitalId(admin.getHospitalId());
        }else{
            if(StringUtils.isEmpty(admin.getRoleIds())){
                return new ArrayList<>();
            }
            String[] split = admin.getRoleIds().split(",");
            List<Integer> groupIds = new ArrayList<>();
            for (String s : split) {
                groupIds.add(Integer.parseInt(s));
            }
            List<HospitalAdminRole> groups = roleRepository.findAllByIdIn(groupIds);
            List<Integer> oauthIds = new ArrayList<>();
            for (HospitalAdminRole group : groups) {
                if(!StringUtils.isEmpty(group.getOauthIds())){
                    String[] split1 = group.getOauthIds().split(",");
                    for (String s : split1) {
                        oauthIds.add(Integer.parseInt(s));
                    }
                }

            }
            menuList = menuRepository.findAllByIdIn(oauthIds);
        }

        return formatAdminMenu(menuList);

    }

    @Override
    public void changeAdminPassword(AdminPasswordDTO adminPasswordDTO) {
        if(!StringUtils.hasText(adminPasswordDTO.getPassword()) || !StringUtils.hasText(adminPasswordDTO.getRePassword())
                || !adminPasswordDTO.getPassword().equals(adminPasswordDTO.getRePassword())){
            throw new NormalException(ResultEnums.PASSWORD_ERROR,"两次密码不一至");
        }

        HospitalAdmin admin = adminRepository.findById(adminPasswordDTO.getAdminId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        if(!admin.getPassword().equals(LocalStringUtil.encrypt(adminPasswordDTO.getOldPassword()))){
            throw new NormalException(ResultEnums.PASSWORD_ERROR);
        }
        admin.setPassword(LocalStringUtil.encrypt(adminPasswordDTO.getPassword()));
        adminRepository.save(admin);
    }

    private   List<HospitalAdminMenuDTO> formatAdminMenu(List<HospitalMenu> list){
        List<HospitalAdminMenuDTO> result = new ArrayList<>();
        for (HospitalMenu menu : list) {
            if (menu.getPid() == 0) {
                HospitalAdminMenuDTO adminMenu = new HospitalAdminMenuDTO();
                BeanUtils.copyProperties(menu, adminMenu);
                for (HospitalMenu child : list) {
                    if (menu.getId().equals(child.getPid())) {
                        HospitalAdminSubMenu subMenu = new HospitalAdminSubMenu();
                        BeanUtils.copyProperties(child, subMenu);
                        adminMenu.getChild().add(subMenu);
                    }
                }
                result.add(adminMenu);
            }
        }
        return result;
    }



    private Specification<HospitalAdmin> generateSpecification(HospitalAdminListForm form) {
        return new Specification<HospitalAdmin>() {
            @Override
            public Predicate toPredicate(Root<HospitalAdmin> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                List<Predicate> predicateList = new ArrayList<>();
                if(form.getHospitalId()!=null && form.getHospitalId()>0){
                    predicateList.add(builder.equal(root.get("hospitalId"), form.getHospitalId()));
                }
                if (!StringUtils.isEmpty(form.getUsername())) {
                    predicateList.add(builder.like(root.get("username"), LocalStringUtil.getLikeString(form.getUsername())));
                }
                if (!StringUtils.isEmpty(form.getKeyword())) {
                    Predicate wb = builder.like(root.get("wb"), LocalStringUtil.getLikeString(form.getKeyword()));
                    Predicate py = builder.like(root.get("py"),  LocalStringUtil.getLikeString(form.getKeyword()));
                    Predicate n = builder.like(root.get("nickname"),  LocalStringUtil.getLikeString(form.getKeyword()));
                    predicateList.add(builder.or(wb, py, n));
                }
                if (form.getType() != null) {
                    predicateList.add(builder.equal(root.get("type"), form.getType()));
                }
                Predicate[] predicateArray = new Predicate[predicateList.size()];
                return query.where(predicateList.toArray(predicateArray)).getRestriction();
            }
        };
    }

    private HospitalAdminRepository adminRepository;
    private HospitalMenuRepository menuRepository;
    private HospitalAdminLogRepository logRepository;
    private RedisService redisService;
    private BaseFeignClient baseFeignClient;

    private HospitalAdminRoleRepository roleRepository;


    @Autowired
    public void setAdminRepository(HospitalAdminRepository adminRepository) {
        this.adminRepository = adminRepository;
    }

    @Autowired
    public void setLogRepository(HospitalAdminLogRepository logRepository) {
        this.logRepository = logRepository;
    }

    @Autowired
    public void setRedisService(RedisService redisService) {
        this.redisService = redisService;
    }

    @Autowired
    public void setBaseFeignClient(BaseFeignClient baseFeignClient) {
        this.baseFeignClient = baseFeignClient;
    }

    @Autowired
    public void setRoleRepository(HospitalAdminRoleRepository roleRepository) {
        this.roleRepository = roleRepository;
    }
    @Autowired
    public void setMenuRepository(HospitalMenuRepository menuRepository) {
        this.menuRepository = menuRepository;
    }
}
